home *** CD-ROM | disk | FTP | other *** search
- /* Address Resolution Protocol (ARP) functions. Sits between IP and
- * Copyright 1991 Phil Karn, KA9Q
- * Level 2, mapping IP to Level 2 addresses for all outgoing datagrams.
- *
- * Mods by G1EMM
- *
- * Mods by SM6RPZ
- * 1992-05-28 - Added interface to arp_lookup() and arp_add().
- * 1992-07-07 - Added arp_timeout(). This function now works according to the
- * last page in RFC826. When an ARP entry times out we will first
- * try an ARP reply before deleting the entry.
- * 1992-07-08 - We will update the ARP table and create new entries each time
- * we hear an ARP request message on the channel. By doing so we
- * will gain knowledge of other stations hardware addresses
- * whithout sending a lot of ARP requests. If we are running RSPF,
- * it will be informed about these "findings".
- * 1992-07-11 - Datagrams to be sent while awaiting an resolution will be put
- * on a queue. The length of this queue in datagrams is defined
- * in arp.h (ARP_QUEUE).
- *
- * 1992-10-27 The above 3 behaviours are now configurable via additional
- * arp subcommands - WG7J
- *
- */
-
- #include "global.h"
- #include "mbuf.h"
- #include "timer.h"
- #include "iface.h"
- #ifdef ETHER
- #include "enet.h"
- #endif
- #include "icmp.h"
- #include "arp.h"
- #include "rspf.h"
- #include "pktdrvr.h"
-
- #if !defined(_lint)
- static char rcsid[] OPTIONAL = "$Id: arp.c,v 1.16 1997/09/07 21:18:28 root Exp root $";
- #endif
-
- extern int Maxarpq;
- extern int ArpExpire;
-
- static void arp_output (struct iface * iface, int16 hardware, uint32 target, char *hw_addr);
- static void arp_timeout (void *p); /* sm6rpz */
-
- extern char ArpDupcall[AXALEN];
-
- /* Hash table headers */
- struct arp_tab *Arp_tab[HASHMOD];
- struct arp_stat Arp_stat = {0, 0, 0, 0, 0, 0, 0};
-
-
- /* Resolve an IP address to a hardware address; if not found,
- * initiate query and return NULLCHAR. If an address is returned, the
- * interface driver may send the packet; if NULLCHAR is returned,
- * res_arp() will have saved the packet on its pending queue,
- * so no further action (like freeing the packet) is necessary.
- */
- char *
- res_arp (iface, hardware, target, bp)
- struct iface *iface; /* Pointer to interface block */
- int16 hardware; /* Hardware type */
- uint32 target; /* Target IP address */
- struct mbuf *bp; /* IP datagram to be queued if unresolved */
- {
- register struct arp_tab *arp;
- struct ip ip;
-
- if ((arp = arp_lookup (hardware, target, iface)) != NULLARP && arp->state == ARP_VALID)
- return arp->hw_addr;
- if (arp != NULLARP) {
- if (len_q (arp->pending) > Maxarpq) {
- /* Earlier packets are already pending, kick
- * this one back as a source quench
- */
- (void) ntohip (&ip, &bp);
- (void) icmp_output (&ip, bp, ICMP_QUENCH, 0, NULL);
- free_p (bp);
- } else
- enqueue (&arp->pending, bp);
- } else {
- /* Create an entry and put the datagram on the
- * queue pending an answer
- */
- arp = arp_add (target, hardware, NULLCHAR, 0, iface);
- enqueue (&arp->pending, bp);
- arp_output (iface, hardware, target, NULLCHAR);
- }
- return NULLCHAR;
- }
-
-
- /* Handle incoming ARP packets. This is almost a direct implementation of
- * the algorithm on page 5 of RFC 826, except for:
- * 1. Outgoing datagrams to unresolved addresses are kept on a queue
- * pending a reply to our ARP request.
- * 2. The names of the fields in the ARP packet were made more mnemonic.
- * 3. Requests for IP addresses listed in our table as "published" are
- * responded to, even if the address is not our own.
- */
- void
- arp_input (iface, bp)
- struct iface *iface;
- struct mbuf *bp;
- {
- struct arp arp;
- struct arp_tab *ap;
- struct arp_type *at;
- int i;
-
- Arp_stat.recv++;
- if (ntoharp (&arp, &bp) == -1) /* Convert into host format */
- return;
- if (arp.hardware >= NHWTYPES) {
- /* Unknown hardware type, ignore */
- Arp_stat.badtype++;
- return;
- }
- at = &Arp_type[arp.hardware];
- if (arp.protocol != at->iptype) {
- /* Unsupported protocol type, ignore */
- Arp_stat.badtype++;
- return;
- }
- if ((int16) uchar (arp.hwalen) > MAXHWALEN || uchar (arp.pralen) != sizeof (int32)) {
- /* Incorrect protocol addr length (different hw addr lengths
- * are OK since AX.25 addresses can be of variable length)
- */
- Arp_stat.badlen++;
- return;
-
- }
- if (arp.sprotaddr == 0L || arp.tprotaddr == 0L) {
- /* We are going to dead-end references for [0.0.0.0], since
- * experience shows that these cause total lock up -- N1BEE
- */
- Arp_stat.badaddr++;
- return;
- }
- if (memcmp (arp.shwaddr, at->bdcst, (size_t) at->hwalen) == 0) {
- /* This guy is trying to say he's got the broadcast address! */
- Arp_stat.badaddr++;
- return;
- }
- /* Try to refine ARP according to section 5.7 in Douglas E.
- * Comers book "Internetworking With TCP/IP", 2nd ed. page 77.
- * I.e we will use all ARP-packets we can see thereby lessen
- * the ARP traffic a little. -- sm6rpz
- */
- if (((ap = arp_lookup (arp.hardware, arp.sprotaddr, iface)) != NULLARP
- && dur_timer (&ap->timer) != 0)
- || ((iface->flags & ARP_EAVESDROP) &&
- ap == NULLARP && arp.opcode != REVARP_REQUEST)
- ) {
- ap = arp_add (arp.sprotaddr, arp.hardware, arp.shwaddr, 0, iface);
- }
- /* See if we're the address they're looking for */
- if (ismyaddr (arp.tprotaddr) != NULLIF) {
- if (ap == NULLARP) /* Only if not already in the table */
- (void) arp_add (arp.sprotaddr, arp.hardware, arp.shwaddr, 0, iface);
-
- if (arp.opcode == ARP_REQUEST) {
- /* Swap sender's and target's (us) hardware and protocol
- * fields, and send the packet back as a reply
- */
- memcpy (arp.thwaddr, arp.shwaddr, (size_t) uchar (arp.hwalen));
- /* Mark the end of the sender's AX.25 address
- * in case he didn't
- */
- if (arp.hardware == ARP_AX25)
- arp.thwaddr[uchar (arp.hwalen) - 1] |= E;
-
- if (iface->type == CL_AX25)
- memcpy (arp.shwaddr, iface->ipcall, (size_t) at->hwalen);
- else
- memcpy (arp.shwaddr, iface->hwaddr, (size_t) at->hwalen);
-
- arp.tprotaddr = arp.sprotaddr;
- arp.sprotaddr = iface->addr;
- if (ismyaddr (arp.sprotaddr) == NULLIF)
- return;
- arp.opcode = ARP_REPLY;
- if ((bp = htonarp (&arp)) == NULLBUF)
- return;
-
- if (iface->forw != NULLIF)
- (void) (*iface->forw->output) (iface->forw,
- arp.thwaddr, iface->forw->ipcall, at->arptype, bp);
- else
- (void) (*iface->output) (iface, arp.thwaddr,
- iface->ipcall, at->arptype, bp);
- Arp_stat.inreq++;
- #ifdef RSPF
- /* Do an RSPF upcall */
- rspfarpupcall (arp.tprotaddr, arp.hardware, NULLIF);
- #endif /* RSPF*/
- } else {
- Arp_stat.replies++;
- #ifdef RSPF
- /* Do an RSPF upcall */
- rspfarpupcall (arp.sprotaddr, arp.hardware, iface);
- #endif /* RSPF*/
- }
- } else if (arp.opcode == ARP_REQUEST
- && (ap = arp_lookup (arp.hardware, arp.tprotaddr, iface)) != NULLARP
- && ap->pub) {
- /* Otherwise, respond if the guy he's looking for is
- * published in our table.
- */
- memcpy (arp.thwaddr, arp.shwaddr, (size_t) uchar (arp.hwalen));
- memcpy (arp.shwaddr, ap->hw_addr, (size_t) at->hwalen);
- arp.tprotaddr = arp.sprotaddr;
- arp.sprotaddr = ap->ip_addr;
- arp.opcode = ARP_REPLY;
- if ((bp = htonarp (&arp)) == NULLBUF)
- return;
- if (iface->forw != NULLIF)
- (void) (*iface->forw->output) (iface->forw,
- arp.thwaddr, iface->forw->ipcall, at->arptype, bp);
- else
- (void) (*iface->output) (iface, arp.thwaddr,
- iface->ipcall, at->arptype, bp);
- Arp_stat.inreq++;
- } else if (arp.opcode == REVARP_REQUEST) {
- for (i = 0; i < HASHMOD; i++)
- for (ap = Arp_tab[i]; ap != NULLARP; ap = ap->next)
- if (memcmp (ap->hw_addr, arp.thwaddr, (size_t) at->hwalen) == 0)
- goto found;
- found:
- if (ap != NULLARP && ap->pub) {
- memcpy (arp.shwaddr, iface->hwaddr, (size_t) at->hwalen);
- arp.tprotaddr = ap->ip_addr;
- arp.sprotaddr = iface->addr;
- arp.opcode = REVARP_REPLY;
- if ((bp = htonarp (&arp)) == NULLBUF)
- return;
- if (iface->forw != NULLIF)
- (void) (*iface->forw->output) (iface->forw,
- arp.thwaddr, iface->forw->ipcall, REVARP_TYPE, bp);
- else
- (void) (*iface->output) (iface, arp.thwaddr,
- iface->ipcall, REVARP_TYPE, bp);
- Arp_stat.inreq++;
- }
- }
- }
-
-
- /* Add an IP-addr / hardware-addr pair to the ARP table */
- struct arp_tab *
- arp_add (ipaddr, hardware, hw_addr, pub, iface)
- uint32 ipaddr; /* IP address, host order */
- int16 hardware; /* Hardware type */
- char *hw_addr; /* Hardware address, if known; NULLCHAR otherwise */
- int pub; /* Publish this entry? */
- struct iface *iface;
- {
- struct mbuf *bp;
- register struct arp_tab *ap;
- struct arp_type *at;
- unsigned hashval;
-
- if (hardware >= NHWTYPES)
- return NULLARP; /* Invalid hardware type */
- at = &Arp_type[hardware];
-
- if ((ap = arp_lookup (hardware, ipaddr, iface)) == NULLARP) {
- /* New entry */
- ap = (struct arp_tab *) callocw (1, sizeof (struct arp_tab));
-
- ap->hw_addr = mallocw ((unsigned) at->hwalen);
- ap->timer.func = arp_timeout;
- ap->timer.arg = ap;
- ap->hardware = hardware;
- ap->ip_addr = ipaddr;
- ap->iface = iface;
-
- /* Put on head of hash chain */
- hashval = hash_ip (ipaddr);
- ap->prev = NULLARP;
- ap->next = Arp_tab[hashval];
- Arp_tab[hashval] = ap;
- if (ap->next != NULLARP) {
- ap->next->prev = ap;
- }
- }
- if (hw_addr == NULLCHAR) {
- /* Await response */
- ap->state = ARP_PENDING;
- set_timer (&ap->timer, Arp_type[hardware].pendtime * 1000L);
- } else {
- /* Response has come in, update entry and run through queue */
- ap->state = ARP_VALID;
- set_timer (&ap->timer, ArpExpire * 1000L);
- memcpy (ap->hw_addr, hw_addr, (size_t) at->hwalen);
- ap->pub = (char) pub;
- while ((bp = dequeue (&ap->pending)) != NULLBUF)
- (void) ip_route (NULLIF, bp, 0);
- }
- start_detached_timer (&ap->timer);
- return ap;
- }
-
-
- /* Remove an entry from the ARP table */
- void
- arp_drop (p)
- void *p;
- {
- register struct arp_tab *ap;
-
- ap = (struct arp_tab *) p;
- if (ap == NULLARP)
- return;
- stop_timer (&ap->timer);/* Shouldn't be necessary */
- if (ap->next != NULLARP)
- ap->next->prev = ap->prev;
- if (ap->prev != NULLARP)
- ap->prev->next = ap->next;
- else
- Arp_tab[hash_ip (ap->ip_addr)] = ap->next;
- free_q (&ap->pending);
- free (ap->hw_addr);
- free ((char *) ap);
- }
-
-
- /* Look up the given IP address in the ARP table */
- struct arp_tab *
- arp_lookup (hardware, ipaddr, iface)
- int16 hardware;
- uint32 ipaddr;
- struct iface *iface;
- {
- register struct arp_tab *ap;
-
- for (ap = Arp_tab[hash_ip (ipaddr)]; ap != NULLARP; ap = ap->next) {
- if (ap->ip_addr == ipaddr && ap->hardware == hardware \
- &&ap->iface == iface)
- break;
- }
- return ap;
- }
-
-
- /* Send an ARP request to resolve IP address target_ip */
- static void
- arp_output (iface, hardware, target, hw_addr)
- struct iface *iface;
- int16 hardware;
- uint32 target;
- char *hw_addr;
- {
- struct arp arp;
- struct mbuf *bp;
- struct arp_type *at;
-
- at = &Arp_type[hardware];
- if (iface->output == NULLFP ((struct iface *, const char *, char *, int16, struct mbuf *)))
- return;
-
- arp.hardware = hardware;
- arp.protocol = at->iptype;
- arp.hwalen = (char) at->hwalen;
- arp.pralen = sizeof (int32);
- arp.opcode = ARP_REQUEST;
-
- if (iface->type == CL_AX25)
- memcpy (arp.shwaddr, iface->ipcall, (size_t) at->hwalen);
- else
- memcpy (arp.shwaddr, iface->hwaddr, (size_t) at->hwalen);
-
- arp.sprotaddr = iface->addr;
- memset (arp.thwaddr, 0, (size_t) at->hwalen);
- arp.tprotaddr = target;
- if ((bp = htonarp (&arp)) == NULLBUF)
- return;
- if ((iface->type == CL_AX25) && *ArpDupcall) {
- struct mbuf *bpc;
-
- bpc = copy_p (bp, ARPLEN + 2 * uchar (arp.hwalen));
- (void) (*iface->output) (iface, ArpDupcall, iface->ipcall, at->arptype, bp);
- bp = bpc;
- }
- if (hw_addr == NULLCHAR) {
- if (iface->type == CL_AX25)
- (void) (*iface->output) (iface, at->bdcst, iface->ipcall, at->arptype, bp);
- else
- (void) (*iface->output) (iface, at->bdcst, iface->hwaddr, at->arptype, bp);
- } else if (iface->type == CL_AX25)
- (void) (*iface->output) (iface, hw_addr, iface->ipcall, at->arptype, bp);
- else
- (void) (*iface->output) (iface, hw_addr, iface->hwaddr, at->arptype, bp);
- Arp_stat.outreq++;
- }
-
-
- /* Called when an ARP entry times out. If an entry has been a valid
- * one we will send out an ARP reply. If this one does not succed,
- * the ARP entry will be dropped. -- sm6rpz
- *
- * Made configurable by WG7J.
- */
- static void
- arp_timeout (p)
- void *p;
- {
- struct arp_type *at;
- char *hwaddr;
- struct arp_tab *ap = (struct arp_tab *) p;
-
- if (ap == NULLARP)
- return;
- stop_timer (&ap->timer);
- /* Check to see if the timer is set to ARPLIFE/ArpExpire or pendtime. Set_timer()
- * adds at least one tick so we must do a little more flexible check.
- */
- if ((ap->iface->flags & ARP_KEEPALIVE) &&
- (dur_timer (&ap->timer) >= /* ARPLIFE */ ArpExpire * 1000L)) {
- at = &Arp_type[ap->hardware];
- set_timer (&ap->timer, at->pendtime * 1000L);
- /* timer functions should NEVER call blocked allocs ! - WG7J */
- if ((hwaddr = (char *) mallocw ((unsigned) at->hwalen)) != NULLCHAR) {
- memcpy (hwaddr, ap->hw_addr, (size_t) at->hwalen);
- arp_output (ap->iface, ap->hardware, ap->ip_addr, hwaddr);
- free (hwaddr); /* clean up */
- }
- start_detached_timer (&ap->timer);
- } else
- arp_drop (ap);
- }
-